package top.jfunc.common.propenv;

import top.jfunc.common.utils.CharsetUtil;

import java.io.InputStream;
import java.util.*;

/**
 * 加载配置工具类，主要提供缓存支持
 * @author 熊诗言
 */
public class PropertiesUtils {
    private static Prop prop = null;
    private static final Map<String, Prop> map = new HashMap<>();

    private PropertiesUtils() {}

    /**
     * Using the properties file. It will loading the properties file if not loading.
     * @see #use(String, String)
     */
    public static Prop use(String fileName) {
        return use(fileName, CharsetUtil.UTF_8);
    }

    /**
     * Using the properties file. It will loading the properties file if not loading.
     * <p>
     * Example:<br>
     * PropertiesUtils.use("config.txt", "UTF-8");<br>
     * PropertiesUtils.use("other_config.txt", "UTF-8");<br><br>
     * String userName = PropertiesUtils.get("userName");<br>
     * String password = PropertiesUtils.get("password");<br><br>
     *
     * userName = PropertiesUtils.use("other_config.txt").get("userName");<br>
     * password = PropertiesUtils.use("other_config.txt").get("password");<br><br>
     *
     * PropertiesUtils.use("com/jfinal/config_in_sub_directory_of_classpath.txt");
     *
     * @param fileName the properties file's name in classpath or the sub directory of classpath
     * @param encoding the encoding
     */
    public static Prop use(String fileName, String encoding) {
        Prop result = map.get(fileName);
        if (result == null) {
            synchronized (map) {
                result = map.get(fileName);
                if (result == null) {
                    InputStream inputStream = new ClasspathEnvStream().loadEnvInputStream(fileName);
                    result = new Prop(inputStream, encoding);
                    map.put(fileName, result);
                    if (PropertiesUtils.prop == null){
                        PropertiesUtils.prop = result;
                    }
                }
            }
        }
        return result;
    }

    public static Prop useLikeSpring(String fileName, BaseEnvStream baseEnvStream) {
        return useLikeSpring(fileName, CharsetUtil.UTF_8, baseEnvStream);
    }

    /**
     * 按照加载的顺序，merge所有的配置项
     * [xx-test.txt 》 test\xx.txt 》 xx.txt]
     */
    public static Prop useLikeSpring(String fileName, String encoding, BaseEnvStream baseEnvStream) {
        Map<String, InputStream> map = baseEnvStream.loadEnvInputStreamMap(fileName);
        List<Prop> props = new ArrayList<>(map.size());
        map.forEach((f,i)->{
            Prop prop = new Prop(i, encoding);
            props.add(prop);
        });

        int size = props.size();
        //最后一个
        Prop lastProp = props.get(size - 1);
        //从倒数第二个开始merge，越往前优先级越高
        for (int i = size - 2; i >= 0; i--) {
            lastProp = lastProp.merge(props.get(i));
        }
        return lastProp;
    }

    public static Prop useless(String fileName) {
        Prop previous = map.remove(fileName);
        if (PropertiesUtils.prop == previous){
            PropertiesUtils.prop = null;
        }
        return previous;
    }

    /**
     * 更根本的方法，传入inputStream即可
     * 但是没有缓存效果了
     * @see BaseEnvStream#loadEnvInputStream(String)
     */
    public static Prop load(InputStream inputStream, String encoding) {
        return new Prop(inputStream, encoding);
    }
    public static Prop load(InputStream inputStream) {
        return new Prop(inputStream, CharsetUtil.UTF_8);
    }

    public static void clear() {
        prop = null;
        map.clear();
    }

    public static Prop getProp() {
        if (prop == null) {
            throw new IllegalStateException("Load properties file by invoking PropertiesUtils.use(String fileName) method first.");
        }
        return prop;
    }

    public static Prop getProp(String fileName) {
        return map.get(fileName);
    }

    public static String get(String key) {
        return getProp().get(key);
    }

    public static String get(String key, String defaultValue) {
        return getProp().get(key, defaultValue);
    }

    public static Integer getInt(String key) {
        return getProp().getInt(key);
    }

    public static Integer getInt(String key, Integer defaultValue) {
        return getProp().getInt(key, defaultValue);
    }

    public static Long getLong(String key) {
        return getProp().getLong(key);
    }

    public static Long getLong(String key, Long defaultValue) {
        return getProp().getLong(key, defaultValue);
    }

    public static Double getDouble(String key) {
        return getProp().getDouble(key);
    }

    public static Double getDouble(String key, Double defaultValue) {
        return getProp().getDouble(key, defaultValue);
    }
    public static Float getFloat(String key) {
        return getProp().getFloat(key);
    }

    public static Float getFloat(String key, Float defaultValue) {
        return getProp().getFloat(key, defaultValue);
    }

    public static Boolean getBoolean(String key) {
        return getProp().getBoolean(key);
    }

    public static Boolean getBoolean(String key, Boolean defaultValue) {
        return getProp().getBoolean(key, defaultValue);
    }

    public static boolean containsKey(String key) {
        return getProp().containsKey(key);
    }

}

